package raytracer;

import scene.Scene;

public class Ray extends Vector3f {

	public Vector3f start;
	public IntersectionResult lastIntersection;

	private static Scene scene;
	private static Vector3f towards;
	private static Vector3f right;
	private static Vector3f p1, p2, p3;
	private static float theta;
	private static float d;

	public static void prepare(Scene sc) {
		scene = sc;

		towards = scene.camera.dir.subtract(scene.camera.pos).normalize();
		theta = scene.camera.halfangle;

		if (scene.camera.pos.x == scene.camera.dir.x
				&& scene.camera.pos.y == scene.camera.dir.y) {
			d = Math.abs(scene.camera.pos.z - scene.camera.dir.z);
		} else {
			d = scene.camera.pos.subtract(scene.camera.dir).length();
		}

		right = towards.cross(scene.camera.up);
		Vector3f dt = towards.scale(d);
		float h = (float) Math.tan(theta / 360f * 2f * Math.PI) * d;
		float l = h * scene.renderInfo.width / scene.renderInfo.height;
		Vector3f dtr = right.scale(l);
		Vector3f dtu = scene.camera.up.scale(h);

		p1 = scene.camera.pos.add(dt).subtract(dtu).subtract(dtr);
		p2 = right.scale(2 * l);
		p3 = scene.camera.up.scale(2 * h);
	}

	public Ray() {
//		x = (x - scene.renderInfo.width / 2f + 0.5f) * -1f;
//		y = y - scene.renderInfo.height / 2f + 0.5f;
//
//		Vector3f p = p1.add(p2.scale((x / scene.renderInfo.width + 0.5f))).add(
//				p3.scale((-y / scene.renderInfo.height + 0.5f)));
//
//		Vector3f v = p.subtract(scene.camera.pos).normalizeLocal();
//
//		this.start = scene.camera.pos;
//		this.x = v.x;
//		this.y = v.y;
//		this.z = v.z;
	}
	
	public void init(float x, float y) {
		x = (x - scene.renderInfo.width / 2f + 0.5f) * -1f;
		y = y - scene.renderInfo.height / 2f + 0.5f;

		Vector3f p = p1.add(p2.scale((x / scene.renderInfo.width + 0.5f))).add(
				p3.scale((-y / scene.renderInfo.height + 0.5f)));

		Vector3f v = p.subtract(scene.camera.pos).normalizeLocal();

		this.start = scene.camera.pos;
		this.x = v.x;
		this.y = v.y;
		this.z = v.z;
	}

	public Ray(Vector3f start, Vector3f v) {
		super(v.x, v.y, v.z);
		this.normalizeLocal();
		this.start = start;
	}

	public static Ray reflect(Ray ray, IntersectionResult intersect) {
		Vector3f r = ray.subtract(intersect.normal.scale(2f * ray
				.dot(intersect.normal)));
		Vector3f v1 = intersect.normal.scale(0.01f);
		return new Ray(v1.addLocal(intersect.pos), r);
	}

	public static Ray refract(Ray ray, IntersectionResult intersection, float a1) {
		Vector3f v = null;

		if (a1 >= 0f) {
			float a2 = (float) Math.sqrt(1
					- Math.pow(1f / intersection.mat.ior, 2) * (1 - a1 * a1));
			v = ray.scale(1 / intersection.mat.ior).add(
					intersection.normal.scale(1 / intersection.mat.ior * a1
							- a2));
		} else {
			float a2 = (float) Math.sqrt(1 - Math.pow(intersection.mat.ior, 2)
					* (1 - a1 * a1));
			v = ray.scale(intersection.mat.ior).add(
					intersection.normal.scale(intersection.mat.ior * a1 + a2));
		}

		Ray r = new Ray(intersection.pos.add(ray.scale(0.001f)).subtractLocal(
				intersection.normal.scale(0.001f)), v);
		return r;
	}

	@Override
	public boolean equals(Object obj) {
		Ray r = (Ray) obj;
		return r.start.equals(this.start) && x == r.x && y == r.y && z == r.z;
	}

	@Override
	public int hashCode() {
		return super.hashCode();
	}

}
